package ar.uba.is2.common.mensaje;

import java.util.Date;
import java.util.HashSet;
import java.util.List;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * Mensaje que se envia desde una TR a la EC. La identificacion univoca de un
 * mensaje esta dada por origen + fechaCreacion + idMensaje + parte.
 */
public class Mensaje {


	public static Mensaje deserializar(String stream){
		for (TipoMensaje tipoMensaje: TipoMensaje.values()) {
			Mensaje msj = tipoMensaje.deserializar(stream);
			if (msj != null) {
				return msj;
			}
		}
		throw new RuntimeException(String.format("El stream %s no es un mensaje serializado", stream));
	}

	public static String serializar(Mensaje msj){
		return msj.getTipoMensaje().serializar(msj);
	}

	/**
	 * Identifica un mensaje. Si un mensaje debe ser particionado, todas las
	 * particiones de un mismo mensaje coincidiran en este dato.
	 */
	private int numeroMensaje;
	
	/**
	 * Diferencia los mensajes de datos de los mensajes de control.
	 *  
	 */
	private TipoMensaje tipoMensaje;

	/**
	 * Cantidad de mensajes que se usan para enviar los datos.
	 */
	private int cantidadPartes;

	/**
	 * El mesaje puede ser particionado para que pueda ser enviado por la red.
	 * Parte identifica el segmento del mensaje
	 */
	private int parte;

	/**
	 * Fecha en la que se crea el mensaje. Un mensaje comparte la misma fecha en
	 * todas sus partes.
	 */
	private Date fechaCreacion;

	/**
	 * Emisor del mensaje.
	 */
	private Componente origen;

	/**
	 * Receptor del mensaje.
	 */
	private Componente destino;

	/**
	 * datos que se recuperaron de los sensores. <idSensor, dato>
	 */
	private List<DatoSensado> datos;

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Mensaje)){
			return false;
		}
		Mensaje that = (Mensaje) obj;

		return new EqualsBuilder().append(this.numeroMensaje,
				that.numeroMensaje).append(this.tipoMensaje, that.tipoMensaje)
				.append(this.cantidadPartes, that.cantidadPartes).append(
						this.parte, that.parte)
				.append(this.origen, that.origen).append(this.destino,
						that.destino).append(this.fechaCreacion,
						that.fechaCreacion).append(new HashSet<DatoSensado>(this.datos), new HashSet<DatoSensado>(that.datos))
				.isEquals();
	}
	
	public int getCantidadPartes() {
		return cantidadPartes;
	}
	
	public List<DatoSensado> getDatos() {
		return datos;
	}
	
	public Componente getDestino() {
		return destino;
	}

	public Date getFechaCreacion() {
		return fechaCreacion;
	}

	public int getNumeroMensaje() {
		return numeroMensaje;
	}

	public Componente getOrigen() {
		return origen;
	}

	public int getParte() {
		return parte;
	}

	public TipoMensaje getTipoMensaje() {
		return tipoMensaje;
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.numeroMensaje).append(
				this.tipoMensaje).append(this.cantidadPartes)
				.append(this.parte).append(this.origen).append(this.destino)
				.append(this.fechaCreacion).append(new HashSet<DatoSensado>(this.datos)).toHashCode();
	}

	public void setCantidadPartes(int cantidadPartes) {
		this.cantidadPartes = cantidadPartes;
	}

	public void setDatos(List<DatoSensado> datos) {
		this.datos = datos;
	}

	public void setDestino(Componente destino) {
		this.destino = destino;
	}

	public void setFechaCreacion(Date fechaCreacion) {
		this.fechaCreacion = fechaCreacion;
	}

	public void setNumeroMensaje(int numeroMensaje) {
		this.numeroMensaje = numeroMensaje;
	}

	public void setOrigen(Componente origen) {
		this.origen = origen;
	}

	public void setParte(int parte) {
		this.parte = parte;
	}

	public void setTipoMensaje(TipoMensaje tipoMensaje) {
		this.tipoMensaje = tipoMensaje;
	}

	@Override
	public String toString() {
		return this.tipoMensaje.serializar(this);
	}

}
